#include "UserLoginSection2.hpp"
#include "MyLogger.hpp"
#include "Configuration.hpp"
#include "MySQLclient.hpp"
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
using namespace std;

namespace wd
{
// 解析客户端发送的"用户名:加密密码"（与客户端格式对齐）
// clientEncrypt - 解析出的客户端加密密码（引用参数，用于返回结果）
bool UserLoginSection2::parseClientData(const string & data,string &username,string &clientEncrypt)
{
   //colonPos是:
   size_t colonPos=data.find(':');
   //没有找到、：在开头、：在结尾
   if(colonPos==string::npos||colonPos==0||colonPos==data.size()-1)
   {
       return false;
   }
   //提取用户名: 从开始到冒号之前的部分
   username=data.substr(0,colonPos);
   //提取加密密码: 从冒号之后到字符串结束的部分
   clientEncrypt=data.substr(colonPos+1);
   return true;
}
// 从数据库查询用户的加密密文（encrypt字段）
string UserLoginSection2::getDbEncrypt(const string&username)
{
//连接数据库
    MySQLclient mysql;
    Configuration* conf=Configuration::getInstance();
    if(!mysql.connect(conf->get("db_host"),
                      stoi(conf->get("db_port")),
                      conf->get("db_user"),
                      conf->get("db_pwd"),
                      conf->get("db_name")))
    {
       LOG_INFO("连接数据库失败");
              return "";
    }

    //连接数据库查看用户是否存在并获取盐值
    std::string sql = "SELECT encrypt FROM user_registry WHERE name = ?"; // 参数化查询防注入
    MYSQL_STMT *stmt=mysql_stmt_init(&mysql._conn);
    if(!stmt||mysql_stmt_prepare(stmt,sql.c_str(),sql.size())!=0)
    {
       LOG_ERROR("查询失败",mysql_stmt_error(stmt));
       mysql_stmt_close(stmt);
              return "";
    }

    //绑定用户名
    MYSQL_BIND bind;
    memset(&bind,0,sizeof(bind));
    bind.buffer_type=MYSQL_TYPE_STRING;
    bind.buffer=(char*)username.c_str();
    bind.buffer_length=username.size();
    if(mysql_stmt_bind_param(stmt,&bind)!=0)
    {
       LOG_ERROR("绑定失败%s",mysql_stmt_error(stmt));
       mysql_stmt_close(stmt);
               return "";
    }

    //执行查询并获取结果
    mysql_stmt_execute(stmt);//将绑定的参数（用户名）发送到服务器执行查询
    //创建 MYSQL_BIND 结构用于接收结果
    MYSQL_BIND resultBind;
    memset(&resultBind,0,sizeof(resultBind));
    //缓冲区
    char encryptBuf[100]={0};
    unsigned long uncryptlen;
    resultBind.buffer_type=MYSQL_TYPE_STRING;
    //数据缓冲区
    resultBind.buffer=encryptBuf;
    resultBind.buffer_length=sizeof(encryptBuf);
    //实际长度指针
    resultBind.length=&uncryptlen;
    //绑定结果到缓冲区
    mysql_stmt_bind_result(stmt,&resultBind);
    //获取行
    string dbEncrypt;
    if(mysql_stmt_fetch(stmt)==0)
    {
        dbEncrypt=string(encryptBuf,uncryptlen);
    }

    //释放资源
    mysql_stmt_close(stmt);
    return dbEncrypt;
}
//校验加密密文，解析登录密码是否正确
void UserLoginSection2::process()
{
   //校验数据包类型必须是客户端登陆阶段2的请求类型
   if(_packet._type!=TASK_TYPE_LOGIN_SECTION2)
   {
        LOG_INFO("阶段2请求失败%d",_packet._type);
        TLV resp(TASK_TYPE_LOGIN_SECTION2_RESP_ERROR,11,"Invalid type");
        _conn->sendInLoop(resp);
        return;
   }
   //解析客户端数据（格式："username:encrypt"，与客户端对齐）
   string username,clientEncrypt;
   if(!parseClientData(_packet._msg,username,clientEncrypt))
   {
        LOG_ERROR("阶段2解析客户端数据失败%s",_packet._msg.c_str());
        TLV resp(TASK_TYPE_LOGIN_SECTION2_RESP_ERROR,11,"Invalid type");
        _conn->sendInLoop(resp);
        return;
   }
   //校验加密密文长度
   if(clientEncrypt.size()!=34)
   {
       LOG_ERROR("阶段2解析数据长度失败%zu",clientEncrypt.size());
        TLV resp(TASK_TYPE_LOGIN_SECTION2_RESP_ERROR,13,"Invalid encrpy");
        _conn->sendInLoop(resp);
        return;
   }
   //从数据库中获取加密密文
   string dbEncrypt=getDbEncrypt(username);
   if(dbEncrypt.empty())
   {
        LOG_ERROR("阶段2获取数据库加密密文失败%s",username.c_str());
        TLV resp(TASK_TYPE_LOGIN_SECTION2_RESP_ERROR,12,"System error");
        _conn->sendInLoop(resp);
        return;
   }
   //校验密码（客户端密文 vs 数据库密文，MD5-Crypt确定性比对）
   LOG_INFO("阶段2客户端密文%s",clientEncrypt.c_str());
   LOG_INFO("阶段2数据库密文%s",dbEncrypt.c_str());
   //登陆成功
   if(clientEncrypt==dbEncrypt)
   {
        LOG_ERROR("阶段2成功%s",username.c_str());
        TLV resp(TASK_TYPE_LOGIN_SECTION2_RESP_OK,12,"Login sucess");
        _conn->sendInLoop(resp);
   }
   else
   {
      LOG_ERROR("阶段2失败%s",username.c_str());
        TLV resp(TASK_TYPE_LOGIN_SECTION2_RESP_ERROR,12,"Invalid password");
        _conn->sendInLoop(resp);
   }
}
}
